package com.zjl.SpringBoot.第03章_Web开发.F_错误处理;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;

/**
 * 目标方法运行期间 出现异常 都会直接catch 表示当前请求结束
 * 获得异常 进行 视图解析流程  再进入视图解析的异常的视图解析
 *
 *  遍历所有的 handlerExceptionResolvers，看谁能处理当前异常
 *      0= {DefaultrrorAttributes@6777} //先执行 将异常信息保存起来 返回null
 *            includeException = null
 *      1 = {HandlerExceptionResolverComposite@6778}//为空在执行
 *        (resolvers = {ArrayList@6784} size = 3
 *          0= {ExceptionHandlerExceptionResolver@6786}
 *           //可以处理带 @ExceptionHandler 注解的异常
 *
 *          1 = {ResponseStatusExceptionResolver@6787}
 *          //可以处理 带 @ResponseStatus 注解的自定义的异常
 *
 *          2 = {DefaultHandlerExceptionResolver@6788}
 *          //处理spring...框架底层的异常
 *
 * 错误的自动配置
 * {@link ErrorMvcAutoConfiguration}
 * {@link BasicErrorController} 存在错误的处理请求转发
 *      server.error.path=xxx 指定路径
 *  其中  errorHtml  和 error 方法就是加载处理异常的类
 *  errorHtml：页面错误
 *  error:  错误 json 信息
 *
 *    可以自定义异常解析器
 *    发现都不能处理异常
 *     ■ 1.最后会发送/error里 (thymeleaf 的功能)
 *       会被底层的{@link BasicErrorController}处理
 *     ■ 2、解析错误视图；遍历所有的  ErrorViewResolver  看谁能解析。
 *     ■ 3、默认的  * {@link DefaultErrorViewResolver} ,
 *         作用是把响应状态码作为错误页的地址，error/错误码.html
 *     ■ 4、模板引擎最终响应这个页面
 *                   error/错误码.html                 /<templates>/error/404.<ext>
 *          找不到找  静态资源/error/错误码.html          /<static>/error/404.html
 *          找不到找  error/4xx.html  5xx.html          /<templates>/error/4xx.<ext>
 *          找不到找  静态资源/error/4xx.html  5xx.html  /<static>/error/4xx.html
 *          找不到  {@link ErrorMvcAutoConfiguration} 的 方法  public View defaultErrorView
 *                  回去找 error的视图
 *      会先找精确的状态码页面 否则找 开头的
 *
 *  最后还是返回 ModelAndView
 *
 *
 */
@Controller
public class 流程 {
    @GetMapping("/er")//http://127.0.0.1:8082/er
    public String loginDemo() throws Exception {
        int a=10/0;
        return "login";
    }
    @GetMapping("/erMy")//http://127.0.0.1:8082/erMy
    public String erMy(){
        throw new 自定义异常();
    }
    @GetMapping("/Exception")//http://127.0.0.1:8082/Exception
    public String xxx() throws Exception {
        throw new Exception("xxx");
    }
}
@ResponseStatus(value = HttpStatus.FORBIDDEN /*状态码*/,reason = "自定义异常***")
class 自定义异常 extends RuntimeException{
    public 自定义异常(String message){
        super(message);
    }
    public 自定义异常(){
    }
}
@Slf4j
@ControllerAdvice//表示这个类 集中处理所有  @Controller 发生的所有错误    适合前后端分离
class 自定义异常处理 {//推荐使用

    //能处理什么异常 这个   算数异常  和空指针异常  会跳到login页面
    @ExceptionHandler({ArithmeticException.class,NullPointerException.class})
    public String HandleArdithException(Exception e, Model model){
        log.error("异常是：{}",e);
        model.addAttribute("err","---自定义异常处理--");
        return "login";//视图地址
    }
}
//可以作为全局异常处理  由于优先级太高   直接返回这个异常
@Order(value = Ordered.LOWEST_PRECEDENCE)//数字越小  优先级越高
@Component
class 自定义异常解析器 implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Object handler, Exception ex) {
        try {
            response.sendError(511,"我喜欢的错误");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ModelAndView();
    }


}

